package com.cxb.carrecorder;

import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.media.MediaRecorder;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OnInfoListener;
import android.view.SurfaceHolder;

import java.io.IOException;

/**
 * Author gebing Date: 2016/03/19
 */
public class CameraUtils {
	private enum State {
		STOPPED, PREPARED, STARTED
	}

	private static int mCameraId;
	private static Camera mCamera;
	private static MediaRecorder mRecorder;
	private static State mPreviewState = State.STOPPED;
	private static State mRecordState = State.STOPPED;
	private static PreviewCallback mPreviewCallback;
	private static OnRecordStateChangeListener onRecordStateChangeListener;

	public static Camera getCurrentCamera() {
		return mCamera;
	}

	/**
	 * 打开指定的摄像头. 如果指定的摄像头当前已打开, 则直接返回; 否则关闭当前摄像头并打开指定的摄像头
	 * 
	 * @param cameraId
	 *            指定的摄像头的id
	 * @return 指定的摄像头对象, null - 指定摄像头不存在无法打开
	 */
	public static Camera openCamera(int cameraId) {
		// 如果指定的摄像头当前已打开, 则直接返回; 否则关闭当前摄像头
		if (mCamera != null && mCameraId == cameraId) {
			return mCamera;
		} else if (mCamera != null) {
			releaseCamera();
		}
		// 打开指定的摄像头
		try {
			mCamera = Camera.open(cameraId);
			mCameraId = cameraId;
			mPreviewState = State.STOPPED;
			mRecordState = State.STOPPED;
			return mCamera;
		} catch (Throwable ex) {
			// 指定摄像头无法打开, 返回null
			return null;
		}
	}

	/**
	 * 返回当前的摄像头. 如果当前没打开任何摄像头, 则打开缺省的后摄像头
	 * 
	 * @return 当前的摄像头对象, null - 当前没有打开任何摄像头, 且缺省的后摄像头也无法打开
	 */
	public static Camera currentCamera() {
		if (mCamera != null) {
			return mCamera;
		} else {
			return openCamera(CameraInfo.CAMERA_FACING_BACK);
		}
	}

	/**
	 * 返回当前的摄像头的id. 如果当前没打开任何摄像头, 则返回-1
	 * 
	 * @return 当前的摄像头的id, -1表示当前没打开任何摄像头
	 */
	public static int currentCameraId() {
		if (mCamera == null)
			return -1;
		return mCameraId;
	}

	/**
	 * 切换并打开当前的摄像头. 如果当前没打开任何摄像头, 则打开缺省的后摄像头
	 * 
	 * @return 切换后的摄像头对象, null - 对应的摄像头无法打开
	 */
	public static Camera switchCamera() {
		if (mCamera == null) {
			return currentCamera();
		} else if (mCameraId == CameraInfo.CAMERA_FACING_BACK) {
			return openCamera(CameraInfo.CAMERA_FACING_FRONT);
		} else {
			return openCamera(CameraInfo.CAMERA_FACING_BACK);
		}
	}

	/**
	 * 释放当前摄像头
	 */
	public static void releaseCamera() {
		if (mCamera != null) {
			stopRecord();
			stopPreview();
			mCamera.release();
			mCamera = null;
			mPreviewState = State.STOPPED;
			mRecordState = State.STOPPED;
		}
	}

	/**
	 * 返回摄像头的预览状态是否为已经设置完参数
	 * 
	 * @return true - 已经设置完参数; false - 未设置完参数或已经启动
	 */
	public static boolean isPreviewPrepared() {
		return mPreviewState == State.PREPARED;
	}

	/**
	 * 返回摄像头的预览状态是否为已经启动
	 * 
	 * @return true - 已经启动; false - 未启动
	 */
	public static boolean isPreviewStarted() {
		return mPreviewState == State.STARTED;
	}

	/**
	 * 准备并设置当前摄像头的预览参数
	 * 
	 * @param holder
	 *            预览的Surface对象
	 * @param width
	 *            预览图像的宽度
	 * @param height
	 *            预览图像的高度
	 * @param format
	 *            预览图像的格式
	 * @param zoom
	 *            预览图像的Zoom级别
	 * @param orientation
	 *            预览图像的方向. 必须为Configuration.ORIENTATION_LANDSCAPE或Configuration.
	 *            ORIENTATION_PORTRAIT
	 * @return true - 成功; false - 失败
	 */
	public static boolean preparePreview(SurfaceHolder holder, int width,
			int height, int format, int zoom, int orientation) {
		// 验证参数合法性
		if (holder == null || width <= 0 || height <= 0 || format <= 0
				|| zoom < 0)
			return false;
		// 如果已经启动, 则不能设置预览参数
		if (isPreviewStarted())
			return false;
		// 获取当前摄像头
		Camera camera = currentCamera();
		if (camera == null)
			return false;
		try {
			// 设置摄像头的预览显示
			camera.setPreviewDisplay(holder);
			// 设置预览图像尺寸/格式
			Parameters parameters = camera.getParameters();
			parameters.setPreviewFormat(format);
			parameters.setPreviewSize(width, height);
			parameters.setZoom(zoom);
			// 设置预览图像方向
			if (orientation != Configuration.ORIENTATION_LANDSCAPE) {
				parameters.set("orientation", "portrait");
				parameters.set("rotation", 90);
				camera.setDisplayOrientation(90);
			} else {
				parameters.set("orientation", "landscape");
				parameters.set("rotation", 0);
				camera.setDisplayOrientation(0);
			}
			camera.setParameters(parameters);
			mPreviewState = State.PREPARED;
			return true;
		} catch (Throwable ex) {
			try {
				camera.setPreviewDisplay(null);
			} catch (IOException ioe) {
			}
			return false;
		}
	}

	/**
	 * 启动当前摄像头的预览
	 * 
	 * @param previewCallback
	 *            预览回调函数
	 * @return true - 成功; false - 失败
	 */
	public static boolean startPreview(PreviewCallback previewCallback) {

		mPreviewCallback = previewCallback;
		// 如果已经启动, 则不能再次启动
		if (isPreviewStarted())
			return false;
		// 获取当前摄像头
		Camera camera = currentCamera();
		if (camera == null)
			return false;
		// 设置预览回调函数
		camera.setPreviewCallback(previewCallback);
		// 启动预览
		camera.startPreview();
		// 设置自动对焦. 为防止摄像头不支持自动对焦, 捕获异常并忽略
		try {
			camera.autoFocus(null);
		} catch (Throwable ex) {
		}
		mPreviewState = State.STARTED;
		return true;
	}

	/**
	 * 启动当前摄像头的预览并使用带缓冲区的预览回调方式
	 * 
	 * @param previewCallback
	 *            预览回调函数. 如果为null, 则不使用预览回调
	 * @param previewBuffer
	 *            预览的图像缓冲区. 如果为null, 则根据预览参数自动分配图像缓冲区
	 * @return true - 成功; false - 失败
	 */
	public static boolean startPreviewWithBuffer(
			PreviewCallback previewCallback, byte[] previewBuffer) {
		mPreviewCallback = previewCallback;
		// 如果已经启动, 则不能再次启动
		if (isPreviewStarted())
			return false;
		// 获取当前摄像头
		Camera camera = currentCamera();
		if (camera == null)
			return false;
		// 设置预览图像缓冲区和回调函数
		if (previewBuffer == null || previewBuffer.length == 0) {
			Parameters parameters = camera.getParameters();
			Size previewSize = parameters.getPreviewSize();
			int previewFormat = parameters.getPreviewFormat();
			previewBuffer = getPreviewBuffer(previewSize.width,
					previewSize.height, previewFormat);
		}
		camera.addCallbackBuffer(previewBuffer);
		camera.setPreviewCallbackWithBuffer(previewCallback);
		// 启动预览
		camera.startPreview();
		// 设置自动对焦. 为防止摄像头不支持自动对焦, 捕获异常并忽略
		try {
			camera.autoFocus(null);
		} catch (Throwable ex) {
		}
		mPreviewState = State.STARTED;
		return true;
	}

	/**
	 * 分配并返回预览的图像缓冲区
	 * 
	 * @param width
	 *            预览图像的宽度
	 * @param height
	 *            预览图像的高度
	 * @param format
	 *            预览图像的格式
	 * @return 分配的预览图像缓冲区, null - 参数错误
	 */
	public static byte[] getPreviewBuffer(int width, int height, int format) {
		if (width <= 0 || height <= 0 || format < 0)
			return null;
		int bufferSize;
		if (format != ImageFormat.YV12) {
			bufferSize = width * height * ImageFormat.getBitsPerPixel(format)
					/ 8;
		} else {
			int yStride = ((width + 15) / 16) * 16;
			int ySize = yStride * height;
			int uvStride = ((yStride / 2 + 15) / 16) * 16;
			int uvSize = uvStride * height / 2;
			bufferSize = ySize + uvSize * 2;
		}
		return new byte[bufferSize];
	}

	/**
	 * 停止当前摄像头的预览
	 */
	public static void stopPreview() {
		if (mCamera != null) {
			// 为防止取消自动对焦失败, 捕获异常并忽略
			try {
				mCamera.cancelAutoFocus();
			} catch (Throwable ex) {
			}
			mCamera.setPreviewCallback(null);
			mCamera.setPreviewCallbackWithBuffer(null);
			mCamera.stopPreview();
			mPreviewState = State.PREPARED;
		}
	}

	/**
	 * 返回当前摄像头的录像对象
	 * 
	 * @return 当前摄像头的录像对象. null - 当前未在录像
	 */
	public static MediaRecorder currentRecorder() {
		return mRecorder;
	}

	/**
	 * 返回当前摄像头录像状态是否为已经设置完参数
	 * 
	 * @return true - 已经设置完参数; false - 未设置完参数或已经在录像
	 */
	public static boolean isRecordPrepared() {
		return mRecorder != null && mRecordState == State.PREPARED;
	}

	/**
	 * 返回当前摄像头是否正在录像
	 * 
	 * @return true - 正在录像; false - 未在录像
	 */
	public static boolean isRecordStarted() {
		return mRecorder != null && mRecordState == State.STARTED;
	}

	/**
	 * 准备并设置当前摄像头的录像参数
	 * 
	 * @param holder
	 *            录像预览的Surface对象
	 * @param width
	 *            录像图像的宽度
	 * @param height
	 *            录像图像的高度
	 * @param enableAudio
	 *            是否录制声音. true - 是; false - 否
	 * @param frameRate
	 *            录像的帧速率. 单位: 帧/秒
	 * @param bitRate
	 *            录像的比特率. 单位: 比特/秒
	 * @param maxDuration
	 *            录像的最大时间. 单位: 毫秒
	 * @param outputFile
	 *            录像的输出文件名
	 * @return true - 成功; false - 失败
	 */
	public static boolean prepareRecord(SurfaceHolder holder, int width,
			int height, boolean enableAudio, int frameRate, int bitRate,
			int maxDuration, String outputFile) {
		// 验证参数

		if (width <= 0 || height <= 0 || frameRate <= 0 || bitRate <= 0
				|| maxDuration <= 0 || outputFile == null
				|| outputFile.isEmpty())
			return false;
		// 如果当前已经在录像, 则不能设置参数
		if (isRecordStarted())
			return false;
		// 获取当前摄像头
		Camera camera = currentCamera();
		if (camera == null)
			return false;
		// 创建Recorder对象
		mRecorder = new MediaRecorder();
		try {
			// 必须先调用unlock, 再调用setCamera
			camera.unlock();
			stopPreview();
//			startPreviewWithBuffer(mPreviewCallback, null);
			mRecorder.setCamera(camera);
			// 必须在setOutputFormat之前调用
			mRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
			if (enableAudio)
				mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
			// 调用setOutputFormat
			mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
			// 必须在setOutputFormat之后调用
			mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
			mRecorder.setVideoSize(width, height);
			mRecorder.setVideoFrameRate(frameRate);
			mRecorder.setVideoEncodingBitRate(bitRate);
			if (enableAudio)
				mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
			mRecorder.setMaxDuration(maxDuration);
			mRecorder.setOutputFile(outputFile);
			if (holder != null)
				mRecorder.setPreviewDisplay(holder.getSurface());
			// 必须在上述调用后执行prepare
			mRecorder.prepare();
			mRecordState = State.PREPARED;
			return true;
		} catch (Throwable ex) {
			mRecorder.release();
			mRecorder = null;
			camera.lock();
			
			if(onRecordStateChangeListener!=null){
				onRecordStateChangeListener.onRecordStateChange(false);
			}
			return false;
		}
	}

	/**
	 * 启动当前摄像头的录像.
	 * 
	 * @param infoListener
	 *            录像出错时的回调函数. 如果为null, 则不使用回调
	 * @param errorListener
	 *            录像出错时的回调函数. 如果为null, 则不使用预览回调
	 * @return true - 成功; false - 失败
	 */
	public static boolean startRecord(OnInfoListener infoListener,
			OnErrorListener errorListener) {
		// 录像参数必须已经设定完毕. 如果尚未设定或当前已经在录像, 则不能再次启动录像
		if (!isRecordPrepared())
			return false;
		mRecorder.start();
		mRecorder.setOnInfoListener(infoListener);
		mRecorder.setOnErrorListener(errorListener);
		if (mPreviewCallback != null) {
			startPreviewWithBuffer(mPreviewCallback, null);
		}
		mRecordState = State.STARTED;
		
		if(onRecordStateChangeListener!=null){
			onRecordStateChangeListener.onRecordStateChange(true);
		}
		return true;
	}

	/**
	 * 停止当前摄像头的录像
	 */
	public static void stopRecord() {
		if (mRecorder != null) {
			// 停止录像
			mRecorder.setOnErrorListener(null);
			mRecorder.setOnInfoListener(null);
			try {
				mRecorder.stop();
			} catch (Throwable ex) {
			}
			// 释放录像对象
			mRecorder.release();
			mRecorder = null;
			mRecordState = State.STOPPED;
			// Re-lock摄像机
			try {
				Camera camera = currentCamera();
				if (camera != null)
					camera.lock();
			} catch (Throwable ex) {
			}
			
			if(onRecordStateChangeListener!=null){
				onRecordStateChangeListener.onRecordStateChange(false);
			}
		}
	}

	public static void setOnRecordStateChangeListener(
			OnRecordStateChangeListener listener) {
		onRecordStateChangeListener = listener;
	}

	public interface OnRecordStateChangeListener {
		public void onRecordStateChange(boolean isRecording);
	}
}
